package io.noties.markwon

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.text.Spanned
import android.text.style.URLSpan
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.TextView
import com.gitee.wsl.common.ui.theme.*
import io.noties.markwon.base.Markwon
import io.noties.markwon.base.html.iframe.AsyncIframeDrawableSpan
import io.noties.markwon.base.image.span.AsyncDrawableSpan
import io.noties.markwon.listener.MediaClickListener
import io.noties.markwon.listener.UrlClickListener
import io.noties.markwon.span.ClickableAudioSpan
import io.noties.markwon.base.html.video.AsyncVideoDrawableSpan
import io.noties.markwon.base.image.span.AsyncRecycleDrawableSpan
import timber.log.Timber
import java.util.*

open class MarkdownViewer : MarkwonMarkdownViewer {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    )

    fun init(markwon:Markwon){
        this.markwon=markwon
    }

    private var mediaClickListener: MediaClickListener? = null
    private var urlClickListener: UrlClickListener? = null
    var allImageSpanList: Array<AsyncRecycleDrawableSpan>? = null


    override fun setMarkdownString(text: CharSequence, afterRender: Runnable?) {
        super.setMarkdownString(text){
            afterRender?.run()
            updateImageSpan()
        }
    }

    protected fun updateImageSpan(){
        allImageSpanList = null
        if (text is Spanned) {
            allImageSpanList = (text as Spanned).getSpans(0, text.length, AsyncRecycleDrawableSpan::class.java)
        }
    }


    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        val widget: TextView = this
        val text = widget.text
        if (text is Spanned) {
            val buffer = text
            val action = event.action
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
                var x = event.x.toInt()
                var y = event.y.toInt()
                x -= widget.totalPaddingLeft
                y -= widget.totalPaddingTop
                x += widget.scrollX
                y += widget.scrollY
                val layout = widget.layout
                val line = layout.getLineForVertical(y)
                if (x <= layout.getLineRight(line)) {
                    val off = layout.getOffsetForHorizontal(line, x.toFloat())
                    if (mediaClickListener != null) {
                        val video = buffer.getSpans(off, off, AsyncVideoDrawableSpan::class.java)
                        if (video.isNotEmpty()) {
                            if (action == MotionEvent.ACTION_UP) {
                                mediaClickListener!!.onVideoClickListener(video[0].videoSrc)
                            }
                            // Don't allow selections on this so always return true
                            return true
                        }
                        val audio = buffer.getSpans(off, off, ClickableAudioSpan::class.java)
                        if (audio.isNotEmpty()) {
                            if (action == MotionEvent.ACTION_UP) {
                                mediaClickListener!!.onAudioClickListener(audio[0].source)
                            }
                            // Don't allow selections on this so always return true
                            return true
                        }
                        val iframe = buffer.getSpans(off, off, AsyncIframeDrawableSpan::class.java)
                        if (iframe.isNotEmpty()) {
                            if (action == MotionEvent.ACTION_UP) {
                                mediaClickListener!!.onIframeClickListener(iframe[0].destination)
                            }
                            // Don't allow selections on this so always return true
                            return true
                        }
                        val image = buffer.getSpans(off, off, AsyncDrawableSpan::class.java)
                        if (image.isNotEmpty()) {
                            if (action == MotionEvent.ACTION_UP) {
                                mediaClickListener!!.onImageclickListener(image[0].drawable.destination)
                            }
                            // Don't allow selections on this so always return true
                            return true
                        }
                    }
                    val link = buffer.getSpans(off, off, URLSpan::class.java)
                    if (link.isNotEmpty()) {
                        if (action == MotionEvent.ACTION_UP) {
                            //link[0].onClick(widget);
                            if (urlClickListener != null) urlClickListener!!.onUrlClicked(link[0].url)
                        }
                        return true
                    }
                }
            }
        }
        return super.onTouchEvent(event)
    }

    fun setMediaClickListener(mediaClickListener: MediaClickListener) {
        this.mediaClickListener = mediaClickListener
    }

    fun setUrlClickListener(urlClickListener: UrlClickListener) {
        this.urlClickListener = urlClickListener
    }

    fun setUrlClickListener(urlClickListener: (urlLink: String)->Unit) {
        this.urlClickListener = object:UrlClickListener{
            override fun onUrlClicked(urlLink: String) {
                urlClickListener(urlLink)
            }
        }
    }


    private val delayMillis = 150
    private var scrollEndTask: Runnable? = null

    private fun onScrollStart() {
        Timber.d("onScrollStart ...")
    }

     private fun onScrollEnd() {
        Timber.d("onScrollEnd ...")
        //if (allImageSpanList == null || allImageSpanList!!.isEmpty()) return
        allImageSpanList?.let{
            updateScrollSpanned()
        }
    }

    override fun scrollTo(x: Int, y: Int) {
        srcollDown = scrollY >= y
        super.scrollTo(x, y)

        allImageSpanList?:return

        if (scrollEndTask == null) {
            onScrollStart()
            scrollEndTask = Runnable {
                scrollEndTask = null
                onScrollEnd()
            }
            postDelayed(scrollEndTask, delayMillis.toLong())
        } else {
            removeCallbacks(scrollEndTask)
            postDelayed(scrollEndTask, delayMillis.toLong())
        }
    }

    var viewHight = 0
    var parentStatY = 0
    var srcollDown = true

    fun parentScrollChanged(startX: Int, startY: Int, oldY: Int, viewHight: Int) {
        if (allImageSpanList == null || allImageSpanList!!.isEmpty()) return
        this.viewHight = viewHight
        parentStatY = startY
        srcollDown = startY >= oldY
        if (scrollEndTask == null) {
            onScrollStart()
            scrollEndTask = Runnable {
                scrollEndTask = null
                onScrollEnd()
            }
            postDelayed(scrollEndTask, delayMillis.toLong())
        } else {
            removeCallbacks(scrollEndTask)
            postDelayed(scrollEndTask, delayMillis.toLong())
        }
    }

    fun updateScrollSpanned(){
        Timber.d("updateScrollSpanned ...")
        if (text is Spanned) {
            val x = this.scrollX
            val y = parentStatY - top
            Timber.d("updateScrollSpanned scrollX:%d,scrollY:%d,viewHight:%d", x, y, viewHight)
            var startLine = 0
            var endLine = 0
            if (srcollDown) {
                startLine = layout.getLineForVertical(y - viewHight)
                endLine = layout.getLineForVertical(y + viewHight * 2)
            } else {
                startLine = layout.getLineForVertical(y - viewHight)
                endLine = layout.getLineForVertical(y + viewHight * 2)
            }
            val startOff = layout.getOffsetForHorizontal(startLine, x.toFloat())
            val endOff = layout.getOffsetForHorizontal(endLine, x.toFloat())
            val imageSpanArray = (text as Spanned).getSpans(startOff, endOff, AsyncRecycleDrawableSpan::class.java)

            Timber.d("updateScrollSpanned onstartOff:${startOff},endOff:${endOff},in imageSpan:${imageSpanArray.size},all imageSpan:${allImageSpanList?.size}")

            imageSpanArray.iterator().forEach {
                it.drawable.reLoad()
            }

            allImageSpanList?.forEach {
                if (it !in imageSpanArray) {
                    it.drawable.recycle()
                }
            }
        }
    }

    override fun onDraw(canvas: Canvas) {
        try {
            super.onDraw(canvas)
        } catch (e: RuntimeException) {
            Timber.d(e)
        }
    }
}